#include <cstdio>
#include <glad/glad.h>
#include <SDL2/SDL.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include "shader/Shader.h"
#include "Model.h"
#include <Singleton.h>
#include <Cache.h>
#include <Texture.h>

int main(int argc, char** argv)
{
    const int windowWidth = 1280;
    const int windowHeight = 1024;
    // 设置 SDL 版本和信息
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
        return -1;
    }
    atexit(SDL_Quit);
    SDL_Window* window = SDL_CreateWindow("SDL 窗口", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
    if(window == nullptr)
    {
        printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
        return -1;
    }
    SDL_GL_CreateContext(window);
    SDL_GL_SetSwapInterval(1);
    //注册OpenGL函数指针
    if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
    {
        printf("Failed to Load GL Loader for glad!");
        return -1;
    }

    Singleton<Cache<std::string, Texture>>::Instance();

    // for apache
//    Model apache = Model("bits/Apache/apache.gltf");
    // for rumya
    Model apache = Model("bits/rumya/RUMYA.gltf");
    // for yuriko
//    Model apache = Model("bits/yuriko/yuriko.gltf");
    // for guard
//    Model apache = Model("bits/Guard/boblampclean.md5mesh");

    Model light = Model("bits/Light/light.gltf");

    glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
    glm::vec3 lightPos = glm::vec3(3.00f, 6.00f, 0.5f);

    glm::vec3 camPos = glm::vec3(0.0f, 0.0f, 20.0f);
    glm::vec3 camRot = glm::vec3(0.0f, 0.0f, 0.0f);

////     正交投影
//    glm::mat4 proj = glm::ortho(-(float)windowWidth / 600.0f,(float)windowWidth / 600.0f,
//                                 -(float)windowHeight / 600.0f,(float)windowHeight / 600.0f,
//                                 0.1f, 100.0f);
    // 透视投影
    glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);

    Shader shader;
    int counter = 0;

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    bool runFlag = true;
    SDL_Event event;

    auto worldCamRot = glm::vec3(0.0f);

    auto camPos_d = camPos;

    //渲染循环
    while(runFlag)
    {
        //处理键盘事件
        while (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
                runFlag = false;


            if (event.type == SDL_KEYDOWN)
            {
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    runFlag = false;
                if (event.key.keysym.sym == SDLK_i)
                    worldCamRot += glm::vec3(0.2f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_k)
                    worldCamRot += glm::vec3(-0.2f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_l)
                    worldCamRot += glm::vec3(0.0f, 0.2f, 0.0f);
                if (event.key.keysym.sym == SDLK_j)
                    worldCamRot += glm::vec3(0.0f, -0.2f, 0.0f);
                if (event.key.keysym.sym == SDLK_u)
                    worldCamRot += glm::vec3(0.0f, 0.0f, 0.2f);
                if (event.key.keysym.sym == SDLK_o)
                    worldCamRot += glm::vec3(0.0f, 0.0f, -0.2f);

                if (event.key.keysym.sym == SDLK_w)
                    camPos += glm::vec3(0.0f, 0.2f, 0.0f);
                if (event.key.keysym.sym == SDLK_s)
                    camPos += glm::vec3(0.0f, -0.2f, 0.0f);
                if (event.key.keysym.sym == SDLK_a)
                    camPos += glm::vec3(-0.2f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_d)
                    camPos += glm::vec3(0.2f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_q)
                    camPos += glm::vec3(0.0f, 0.0f, 0.2f);
                if (event.key.keysym.sym == SDLK_e)
                    camPos += glm::vec3(0.0f, 0.0f, -0.2f);

                if(event.key.keysym.sym == SDLK_SPACE)
                {
                    camPos = camPos_d;
                    worldCamRot = glm::vec3(0.0f, 0.0f, 0.0f);
                }

            }
        }

        glClearColor(0.22f, 0.33f, 0.33f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glm::mat4 view(1.0f);
        glm::mat4 worldCamRotM = glm::eulerAngleXYZ(worldCamRot.x, worldCamRot.y, worldCamRot.z);
        view = view * worldCamRotM;
        view = glm::translate(view, camPos);
        view = view * glm::eulerAngleXYZ(camRot.x, camRot.y, camRot.z);
        view = glm::inverse(view);

        shader.UseShader();

        shader.setVec3("dirLight.direction", -lightPos);
        shader.setVec3("dirLight.color", lightColor * 0.0f);

        shader.setVec3("pointLight.position", lightPos);
        shader.setVec3("pointLight.color", lightColor);
        shader.setFloat("pointLight.constant",  1.0f);
        shader.setFloat("pointLight.linear",    0.009f);
        shader.setFloat("pointLight.quadratic", 0.0032f);

        shader.setVec3("spotLight.position", lightPos);
        shader.setVec3("spotLight.direction", -lightPos);
        shader.setVec3("spotLight.color", lightColor * 0.0f);
        shader.setFloat("spotLight.innerCutoff", glm::cos(glm::radians(5.5f)));
        shader.setFloat("spotLight.outerCutoff", glm::cos(glm::radians(15.5f)));

        shader.setFloat("material.ambientStrength", 0.3f);
        shader.setFloat("material.diffuseStrength", 0.7f);
        shader.setFloat("material.specularStrength", 1.0f);
        shader.setFloat("material.shininess", 32.0f);

        shader.setMat4("projMatrix", proj);
        shader.setMat4("viewMatrix", view);
        shader.setVec3("viewPos", glm::mat3(worldCamRotM) * camPos);

        {
            glm::mat4 model(1.0f);

            // for apache
//            model = glm::scale(model, glm::vec3(1.0f));
            // for rumya
            model = glm::scale(model, glm::vec3(5.0f));
            //for yuriko
//            model = glm::translate(model, glm::vec3(0.0f, -2.0f, 0.0f));
//            model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f));
//            model = glm::scale(model, glm::vec3(0.2f));
            //for guard
//            model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
//            model = glm::scale(model, glm::vec3(0.1f));

            shader.setBool("isPointLight", false);
            apache.SetModelMatrix(model);

            // for apache
//            apache.Play("ApacheRotor", counter);
            // for rumya
            apache.Play("b_catidle-loop", counter);
            // for yuriko
//            apache.Play("JUCOMMANDOTECH1_SKLAction", counter);
            // for guard
//            apache.Play("", counter);

            apache.Draw(shader);
        }


        {
            glm::mat4 model(1.0f);
            model = glm::translate(model, lightPos);
            model = glm::scale(model, glm::vec3(0.25f));
            shader.setBool("isPointLight", true);
            light.SetModelMatrix(model);
            light.Draw(shader);
        }

        counter++;

        SDL_GL_SwapWindow(window);
        SDL_Delay(33);
    }

    SDL_DestroyWindow(window);
    return 0;
}
